package cn.alex.sso.project.controller;

import cn.alex.sso.framework.core.Result;
import cn.alex.sso.framework.enums.StatusEnum;
import cn.alex.sso.framework.global.ServiceException;
import cn.alex.sso.framework.util.BrowserUtil;
import cn.alex.sso.project.entity.SysClient;
import cn.alex.sso.project.entity.SysLoginLog;
import cn.alex.sso.project.entity.SysUser;
import cn.alex.sso.project.service.SysClientService;
import cn.alex.sso.project.service.SysLoginLogService;
import cn.alex.sso.project.service.SysUserService;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.spring.SpringMVCUtil;
import cn.dev33.satoken.sso.config.SaSsoServerConfig;
import cn.dev33.satoken.sso.processor.SaSsoServerProcessor;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.dtflys.forest.Forest;
import com.jthinking.common.util.ip.IPInfoUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.AsyncContext;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;

/**
 * SSO服务端处理器
 *
 * @author Alex
 * @date 2024/9/24 14:44
 */
@Slf4j
@RestController
@RequiredArgsConstructor
public class SsoServerController {
    private final SysClientService clientService;
    private final SysUserService userService;
    private final SysLoginLogService loginLogService;

    @RequestMapping("/sso/*")
    public Object ssoRequest() {
        return SaSsoServerProcessor.instance.dister();
    }

    @Autowired
    private void ssoServerConfig(SaSsoServerConfig sso) {
        // 配置未登录时返回的View
        sso.notLoginView = () -> new ModelAndView("login.html");
        // 配置Http请求处理器
        sso.sendHttp = url -> {
            try {
                return Forest.get(url).executeAsString();
            } catch (Exception e) {
                log.error("请求[{}]失败", url, e);
                return null;
            }
        };
        // 配置登录处理器
        sso.doLoginHandle = (name, pwd) -> {
            HttpServletRequest req = SpringMVCUtil.getRequest();
            SysLoginLog loginLog = new SysLoginLog()
                    .setUsername(name)
                    .setNickName("-")
                    .setStatus(StatusEnum.YES.getStatus())
                    .setErrorMsg("");
            String client = SaHolder.getRequest().getParam("client");
            SysClient sysClient;

            // 如果client等于空, 默认登录统一认证平台
            if (StrUtil.isBlank(client)) {
                sysClient = clientService.getLcSsoServerClient();
            } else {
                sysClient = clientService.getById(client);
                if (ObjUtil.isEmpty(sysClient)) {
                    return Result.err("您当前登录的应用不存在");
                }
                loginLog.setClientId(sysClient.getId());
            }


            // 判断用户名密码是否正确
            SysUser user = userService.getUserByUsername(name);
            if (ObjUtil.isNotEmpty(user)) {
                loginLog.setUserId(user.getId());
                loginLog.setNickName(user.getNickName());
            }

            try {
                if (ObjUtil.isEmpty(user) || !BCrypt.checkpw(pwd, user.getPassword())) {
                    throw new ServiceException("用户名或密码错误");
                }

                // 账号被禁用
                if (user.getStatus().equals(StatusEnum.NO.getStatus())) {
                    throw new ServiceException("您的账号已被管理员禁用，请联系管理员!");
                }

                // 判断用户是否有当前应用权限
                boolean existsUserInClient = userService.existsUserInClient(user.getId(), sysClient.getId());
                if (!existsUserInClient) {
                    throw new ServiceException("您没有当前应用访问权限，请联系管理员开通！");
                }
            } catch (Exception e) {
                loginLog.setStatus(StatusEnum.NO.getStatus());
                loginLog.setErrorMsg(e.getMessage());
            }

            try {
                if (!loginLog.getStatus().equals(StatusEnum.NO.getStatus())) {
                    // 登录
                    StpUtil.login(user.getId());

                    // 用户信息写入Session
                    StpUtil.getSession().set("user", user);

                    // 异步更新最后登录时间
                    userService.updateLastLoginTime(user.getId());
                    return Result.ok("登录成功");
                } else {
                    return Result.err(loginLog.getErrorMsg());
                }
            } finally {
                // 记录登录日志
                AsyncContext asyncContext = req.startAsync();
                asyncContext.start(() -> {
                    loginLog.setIpAddress(BrowserUtil.getIpAddress(req));
                    loginLog.setGeoLocation(IPInfoUtils.getIpInfo(loginLog.getIpAddress()).getAddress());
                    loginLog.setDeviceInfo(BrowserUtil.getOs(req));
                    loginLog.setBrowserInfo(BrowserUtil.getBrowser(req));
                    loginLog.setLoginTime(new Date());
                    loginLogService.save(loginLog);
                    asyncContext.complete();
                });
            }
        };
    }
}
